﻿namespace SecutityLib
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// Poly Alphabetic technique
    /// </summary>
    public class PolyAlphabetic : EncodingDecodingTech
    {
        /// <summary>
        /// Gets or sets a value indicating whether [repeat key].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [repeat key]; otherwise, <c>false</c>.
        /// </value>
        public bool RepeatKey
        {
            get;
            set;
        }

        /// <summary>
        /// Encodes the specified plain text.
        /// </summary>
        /// <param name="plainText">The plain text.</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Encode(string plainText, string key)
        {
            this.CheckText(plainText, key);

            if (!this.RepeatKey)
            {
                return this.AutoKeyEnc(key, plainText);
            }
            else
            {
                return this.RepKeyEnc(key, plainText);
            }
        }

        /// <summary>
        /// Decodes the specified plain text.
        /// </summary>
        /// <param name="cipherText">the cipher text</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Decode(string cipherText, string key)
        {
            this.CheckText(cipherText, key);

            if (!this.RepeatKey)
            {
                return this.AutoKeyDec(key, cipherText);
            }
            else
            {
                return this.RepKeyDec(key, cipherText);
            }
        }

        /// <summary>
        /// Generates the matrix.
        /// </summary>
        /// <returns>the key matrix</returns>
        private static char[,] GenerateMatrix()
        {
            int col = 96;
            int row = 0;
            char[,] returnedMatrix = new char[26, 26];
            for (int i = 0; i < 26; i++)
            {
                col++;
                for (int j = 0; j < 26; j++)
                {
                    if (col + j > 122)
                    {
                        row = col + j - 26;
                    }
                    else
                    {
                        row = col + j;
                    }

                    returnedMatrix[i, j] = (char)row;
                }
            }

            return returnedMatrix;
        }

        /// <summary>
        /// Autoes the key enc.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="plain">The plain.</param>
        /// <returns>the cipher text</returns>
        private string AutoKeyEnc(string key, string plain)
        {
            int col = 0, row = 0;
            string cipher = null;
            char[,] vignere = GenerateMatrix();
            int cnt = plain.Length - key.Length;
            for (int i = 0; i < cnt; i++)
            {
                if (plain[i] != ' ')
                {
                    key += plain[i];
                }
                else
                {
                    cnt++;
                }
            }

            for (int i = 0; i < plain.Length; i++)
            {
                col = key[i] - 97;
                row = plain[i] - 97;
                cipher += vignere[col, row];
            }

            return cipher;
        }

        /// <summary>
        /// Autoes the key dec.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="cipher">The cipher.</param>
        /// <returns>the plain text</returns>
        private string AutoKeyDec(string key, string cipher)
        {
            string plain = null;
            char[,] vignere = GenerateMatrix();

            int cnt = cipher.Length - key.Length;

            for (int i = 0; i < cipher.Length; i++)
            {
                for (int j = 0; j < 26; j++)
                {
                    if (vignere[(key[i] - 97), j] == cipher[i])
                    {
                        plain += (char)(j + 97);
                        if (cnt >= 0)
                        {
                            key += (char)(j + 97);
                            cnt--;
                        }

                        break;
                    }
                }
            }

            return plain;
        }

        /// <summary>
        /// Reps the key enc.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="plain">The plain.</param>
        /// <returns>the cipher text</returns>
        private string RepKeyEnc(string key, string plain)
        {
            int col = 0, row = 0;
            string cipher = null;
            char[,] vignere = GenerateMatrix();
            int cnt = plain.Length - key.Length;
            int tempKeyLen = key.Length;
            for (int i = 0; i < cnt; i++)
            {
                key += key[i % tempKeyLen];
            }

            for (int i = 0; i < plain.Length; i++)
            {
                col = key[i] - 97;
                row = plain[i] - 97;
                cipher += vignere[col, row];
            }

            return cipher;
        }

        /// <summary>
        /// Reps the key dec.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="cipher">The cipher.</param>
        /// <returns>the plain text</returns>
        private string RepKeyDec(string key, string cipher)
        {
            string plain = null;
            char[,] vignere = GenerateMatrix();

            int cnt = cipher.Length - key.Length;
            int tempKeyLen = key.Length;
            for (int i = 0; i < cnt; i++)
            {
                key += key[i % tempKeyLen];
            }

            for (int i = 0; i < cipher.Length; i++)
            {
                for (int j = 0; j < 26; j++)
                {
                    if (vignere[(key[i] - 97), j] == cipher[i])
                    {
                        plain += (char)(j + 97);
                        break;
                    }
                }
            }

            return plain;
        }

        /// <summary>
        /// Checks the text.
        /// </summary>
        /// <param name="plain">The plain.</param>
        /// <param name="key">The key.</param>
        private void CheckText(string plain, string key)
        {
            for (int i = 0; i < plain.Length; i++)
            {
                if (plain[i] < 'a' || plain[i] > 'z')
                {
                    throw new Exception("Text must be char");
                }
            }

            for (int i = 0; i < key.Length; i++)
            {
                if (key[i] < 'a' || key[i] > 'z')
                {
                    throw new Exception("key must be text");
                }
            }
        }
    }
}
